/*
 *    Copyright (c) 2018-2025, proj All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the proj developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: proj
 */
package com.ykht.proj.admin.service.impl;

import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ykht.proj.admin.api.entity.SysFile;
import com.ykht.proj.admin.api.vo.FileVo;
import com.ykht.proj.admin.mapper.SysFileMapper;
import com.ykht.proj.admin.service.SysFileService;
import com.ykht.proj.admin.utils.VideoUtil;
import com.ykht.proj.common.core.constant.CommonConstants;
import com.ykht.proj.common.core.util.R;
import com.ykht.proj.common.oss.OssProperties;
import com.ykht.proj.common.oss.service.OssTemplate;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import ws.schild.jave.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.List;

/**
 * 文件管理
 *
 * @author Luckly
 * @date 2019-06-18 17:18:42
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {

	private final OssProperties ossProperties;

	private final OssTemplate minioTemplate;

	/**
	 * 上传文件
	 * @param file
	 * @return
	 */
	@Override
	public R uploadFile(MultipartFile file) {
		String fileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename());
		Map<String, String> resultMap = new HashMap<>(4);
		resultMap.put("bucketName", ossProperties.getBucketName());
		resultMap.put("fileName", fileName);
		resultMap.put("url", String.format("/admin/sys-file/%s/%s", ossProperties.getBucketName(), fileName));

		try {
			minioTemplate.putObject(ossProperties.getBucketName(), fileName, file.getInputStream());
			// 文件管理数据记录,收集管理追踪文件
			fileLog(file, fileName);
		}
		catch (Exception e) {
			log.error("上传失败", e);
			e.printStackTrace();
			return R.failed(e.getLocalizedMessage());
		}
		return R.ok(resultMap);
	}

	@Value("${oss.baseHost}")
	private String ossBaseHost;
	@Override
	public R upload4url(MultipartFile file) {
		String fileName = null;
		try {
			fileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename());
			Map<String, String> resultMap = new HashMap<>(4);
			resultMap.put("bucketName", ossProperties.getBucketName());
			resultMap.put("fileName", fileName);
			resultMap.put("url", String.format("/admin/sys-file/%s/%s", ossProperties.getBucketName(), fileName));

			minioTemplate.putObject(ossProperties.getBucketName(), fileName, file.getInputStream());
			// 文件管理数据记录,收集管理追踪文件
			fileName = ossBaseHost+ resultMap.get("fileName");
			log.info("上传文件地址为：{}",fileName);
			return R.ok(fileName);
		} catch (Exception e) {
			e.printStackTrace();
			return R.failed(fileName);
		}
	}
	@Override
	public R upload4url(File file) {
		String fileName = null;
		try {
			fileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getName());
			Map<String, String> resultMap = new HashMap<>(4);
			resultMap.put("bucketName", ossProperties.getBucketName());
			resultMap.put("fileName", fileName);
			resultMap.put("url", String.format("/admin/sys-file/%s/%s", ossProperties.getBucketName(), fileName));

			minioTemplate.putObject(ossProperties.getBucketName(), fileName, FileUtil.getInputStream(file));
			// 文件管理数据记录,收集管理追踪文件
			fileName = ossBaseHost+ resultMap.get("fileName");
			log.info("上传文件地址为：{}",fileName);
			return R.ok(fileName);
		} catch (Exception e) {
			e.printStackTrace();
			return R.failed(fileName);
		}
	}
	@Override
	public String upload4cms(MultipartFile file) {
		String fileName = null;
		try {
			fileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename());
			Map<String, String> resultMap = new HashMap<>(4);
			resultMap.put("fileName", fileName);

			minioTemplate.putObject(ossProperties.getBucketName(), fileName, file.getInputStream());
			// 文件管理数据记录,收集管理追踪文件
			fileName = ossBaseHost+ resultMap.get("fileName");
			log.info("上传文件地址为：{}",fileName);

			Map<String ,Object> result = new HashMap<>();
			result.put("group","");
			result.put("original",file.getResource());
			result.put("size",file.getSize());
			result.put("state","SUCCESS");
			result.put("title",file.getSize());
			result.put("type",file.getSize());
			result.put("url",fileName);
			return JSONUtil.toJsonStr(result);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public R uploadLimit4url(FileVo fileVo) throws Exception {
		R r  = this.check(fileVo);
		if (r.getCode()== CommonConstants.FAIL) return r;
		return this.upload4url(fileVo.getFile());
	}

	@Override
	public R uploadFirstFrame4url(String videoUrl) throws Exception {
		long startTime=System.currentTimeMillis();  //获取开始时间
		File videoPic = null;
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			URL url = new URL(videoUrl);
			URLConnection connection = url.openConnection();
			inputStream = connection.getInputStream();
			File video = new File("video.mp4");
			outputStream = new FileOutputStream(video);
			System.out.println(System.currentTimeMillis()-startTime);
			int bytesRead = 0;
			byte[] buffer = new byte[1024];
			while ((bytesRead = inputStream.read(buffer, 0, 1024)) != -1) {
				outputStream.write(buffer, 0, bytesRead);
			}
			outputStream.close();
			inputStream.close();
			String picPath = "video.jpg";
			videoPic = VideoUtil.getVideoPic(video, picPath);
			video.delete();
			video=null;

		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			outputStream.close();
			inputStream.close();
		}
		R r = this.upload4url(videoPic);
		long endTime=System.currentTimeMillis();//获取结束时间
		System.out.println("程序运行时间： "+(startTime-endTime)+"ms");
		return r;
	}

	/**
	 * 根据前端穿的参数来校验
	 * @param fileVo
	 * @return
	 * @throws Exception
	 */
	private R check(FileVo fileVo) throws Exception {
		List<String> vType = Arrays.asList(/*"avi","mov","rmvb",*/"mp4");
		List<String> iType = Arrays.asList("png","jpg","jpeg","gif");
		MultipartFile file = fileVo.getFile();
		String prefixe= file.getContentType().split("/")[0];//视频还是图片
		String suffixe= file.getContentType().split("/")[1];//文件后缀类型
		Long size = file.getSize()/1024;//单位KB，文件大小
		Integer height = 0;
		Integer width = 0;
		String typeName = "";
		String sizeText = "当前%s大小:%sKB,规定不大于:%sKB";
		String heightText = "当前%s高度:%sPX,规定高度:%sPX";
		String widthText = "当前%s宽度:%sPX,规定宽度:%sPX";

		String type = FileTypeUtil.getType(fileVo.getFile().getInputStream());
		System.out.println("------------------"+type);
		log.info("识别文件类型type：{}，suffixe：{}",type,suffixe);
		if (type == null) type = suffixe;
		switch (prefixe){
			case "video":
				typeName = "视频";
				if (fileVo.getFileType()!=null && !fileVo.getFileType().equals(prefixe)) return R.failed(String.format("请上传图片"));
				if (!vType.contains(type.toLowerCase())) return R.failed(String.format(typeName+"上传仅支持%s格式", vType));

				MultimediaObject multimediaObject = new MultimediaObject(FileUtil.writeBytes(file.getBytes(),file.getOriginalFilename()));
				MultimediaInfo info = multimediaObject.getInfo();
				height = info.getVideo().getSize().getHeight();
				width = info.getVideo().getSize().getWidth();
				break;
			case "image":
				typeName = "图片";
				if (fileVo.getFileType()!=null && !fileVo.getFileType().equals(prefixe)) return R.failed(String.format("请上传视频"));
				if (!iType.contains(type.toLowerCase())) return R.failed(String.format(typeName+"上传仅支持%s格式", iType));
				BufferedImage img = ImageIO.read(file.getInputStream());
				height = img.getHeight();
				width = img.getWidth();
				break;
			default:
//				return R.failed(String.format("请上传正确的文件!"));
				break;
		}
		if (fileVo.getSize()!=null && size>fileVo.getSize()) return R.failed(String.format(sizeText,typeName,size,fileVo.getSize()));
		if (fileVo.getHeight()!=null && !height.equals(fileVo.getHeight())) return R.failed(String.format(heightText,typeName,height,fileVo.getHeight()));
		if (fileVo.getWidth()!=null && !width.equals(fileVo.getWidth())) return R.failed(String.format(widthText,typeName,width,fileVo.getWidth()));
		return R.ok();
	}




	/**
	 * 读取文件
	 * @param bucket
	 * @param fileName
	 * @param response
	 */
	@Override
	public void getFile(String bucket, String fileName, HttpServletResponse response) {
		try (InputStream inputStream = minioTemplate.getObject(bucket, fileName)) {
			response.setContentType("application/octet-stream; charset=UTF-8");
			IoUtil.copy(inputStream, response.getOutputStream());
		}
		catch (Exception e) {
			log.error("文件读取异常: {}", e.getLocalizedMessage());
			e.printStackTrace();
		}
	}

	/**
	 * 删除文件
	 * @param id
	 * @return
	 */
	@Override
	@SneakyThrows
	@Transactional(rollbackFor = Exception.class)
	public Boolean deleteFile(Long id) {
		SysFile file = this.getById(id);
		minioTemplate.removeObject(ossProperties.getBucketName(), file.getFileName());
		return this.removeById(id);
	}



	/**
	 * 文件管理数据记录,收集管理追踪文件
	 * @param file 上传文件格式
	 * @param fileName 文件名
	 */
	private void fileLog(MultipartFile file, String fileName) {
		SysFile sysFile = new SysFile();
		// 原文件名
		String original = file.getOriginalFilename();
		sysFile.setFileName(fileName);
		sysFile.setOriginal(original);
		sysFile.setFileSize(file.getSize());
		sysFile.setType(FileUtil.extName(original));
		sysFile.setBucketName(ossProperties.getBucketName());
//		sysFile.setCreateUser(SecurityUtils.getUser().getUsername());
		this.save(sysFile);
	}

}
